/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package foalvleditor.core;

import foalvleditor.core.impl.LevelModel;
import foalvleditor.gameobjects.AbstractGameObject;
import foalvleditor.gameobjects.Bitmap;
import foalvleditor.gameobjects.GameEvent;
import foalvleditor.gameobjects.GameEvent.EventType;
import foalvleditor.gameobjects.GameObjectFactory;
import foalvleditor.gameobjects.GameObjectFactory.GameObjectType;
import java.awt.Point;
import java.io.*;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.regex.Pattern;
import javax.swing.JOptionPane;
import javax.xml.parsers.*;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;


/**
 * @author Mikael
 */

public final class LevelParser {

    private static final LevelModel levelModel=LevelModel.getInstance();
    private static final LevelParser parserInstance=new LevelParser();

    /**
     * parses the XML instance file giving an ArrayList containing
     * list of objects which can be displayed in the editor
     *
     * @indata XML instance file
     * @return ArrayList containing bitmap objects
     * @return ArrayList containing
     */

    private LevelParser(){}

    public static LevelParser getInstance(){
        return parserInstance;
    }

    public void parseLevel(String filename) {


    	ArrayList<AbstractGameObject> parsedLevel=new ArrayList<AbstractGameObject>();
        String levelName="";
        ArrayList<GameEvent> parsedEvents=new ArrayList<GameEvent>();
        AbstractGameObject background=null;


    try {

    	File levelFile=new File(filename);              //loads the instance
        Document xmldoc=DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(levelFile); //creates api to parse xml
        NodeList gameObjectsList=xmldoc.getElementsByTagName("objectBit");           // parses the xml file looking for <object>
        int posx=0,posy=0,layer=0;
        for (int i=0; i<gameObjectsList.getLength(); i++){                        // parses the object list
            NamedNodeMap attributeList=gameObjectsList.item(i).getAttributes();
            String thisObjectType=attributeList.getNamedItem("objectType").getNodeValue();
            String thisObjectPos=attributeList.getNamedItem("position").getNodeValue();
            String thisObjectID=attributeList.getNamedItem("bitmap").getNodeValue();
            String thisObjectLayer=attributeList.getNamedItem("layer").getNodeValue();
            Scanner sc=new Scanner(thisObjectPos.substring(0,thisObjectPos.indexOf(',')));
            posx=sc.nextInt();
            sc=new Scanner(thisObjectPos.substring(thisObjectPos.indexOf(',')+1, thisObjectPos.length()));
            posy=sc.nextInt();
            sc=new Scanner(thisObjectLayer);
            layer=sc.nextInt();
            Point newPosition=new Point(posx,posy);
            Bitmap newBitmap=new Bitmap(thisObjectID);
            GameObjectType gameObjectType=null;
            if(thisObjectType.contains("Scenery")) background=
                    GameObjectFactory.createGameObject(GameObjectFactory.GameObjectType.Scenery,
                    newPosition, newBitmap, layer);
            else if(thisObjectType.contains("Player")) gameObjectType=GameObjectType.PlayerObject;
            else if(thisObjectType.contains("Enemy")) gameObjectType=GameObjectType.Enemy;
            else if(thisObjectType.contains("Door")) gameObjectType=GameObjectType.Door;
            else if(thisObjectType.contains("Key")) gameObjectType=GameObjectType.Key;
            else if(thisObjectType.equals("ReverseGravitable")) gameObjectType=GameObjectType.ReverseGravitable;
            else if(thisObjectType.equals("Gravitable")) gameObjectType=GameObjectType.Gravitable;
            else if(thisObjectType.contains("Spike")) gameObjectType=GameObjectType.Spike;
            else if(thisObjectType.contains("Stationary")) gameObjectType=GameObjectType.StationaryObstacle;
            else if(thisObjectType.contains("Lever")) gameObjectType=GameObjectType.Lever;
            else if(thisObjectType.contains("Push")) gameObjectType=GameObjectType.Push;

            if (!thisObjectType.contains("Scenery"))
               parsedLevel.add(GameObjectFactory.createGameObject(gameObjectType,
                       newPosition, newBitmap, layer));
        }

      try
      {
          levelName=xmldoc.getElementsByTagName("plaintag").item(0).
                  getFirstChild().getNodeValue();
      }

      catch (Exception e){
          levelName="(Untitled)";
          e.printStackTrace();
      }

      try{
          // parses the xml file looking for events
          NodeList eventsList=xmldoc.getElementsByTagName("event");
      for (int i=0; i<eventsList.getLength(); i++){                        // parses the object list
          ArrayList<AbstractGameObject> affectedObjects=new ArrayList<AbstractGameObject>();
          EventType type;
          NamedNodeMap attributeList=eventsList.item(i).getAttributes();
          String evtTypeString=attributeList.getNamedItem("eventType").getNodeValue();
          String objects=attributeList.getNamedItem("objects").getNodeValue();
          Pattern p = Pattern.compile(",");
          String[]numbers=p.split(objects);
          ArrayList<Integer> parsedObjects=new ArrayList<Integer>();
          for (int j=0; j<numbers.length; j++)
          {
              parsedObjects.add(Integer.parseInt(numbers[j]));
          }
          for (int k=0; k<parsedObjects.size(); k++)
          {
              affectedObjects.add(parsedLevel.get(parsedObjects.get(k)));
          }
          if (evtTypeString.equals("Switch")) {
              String bind=attributeList.getNamedItem("bind").getNodeValue();
              AbstractGameObject firstObject=parsedLevel.get(Integer.parseInt(bind));
              type=EventType.SWITCH;
              parsedEvents.add(new GameEvent(type, firstObject, affectedObjects));
            }
          else if (evtTypeString.equals("Chain")) {
              type=EventType.CHAIN;
              AbstractGameObject firstObject=affectedObjects.get(0);
              affectedObjects.remove(0);
              parsedEvents.add(new GameEvent(type, firstObject, affectedObjects));
          }

          }
      }
      catch (Exception e){
          JOptionPane.showMessageDialog(null, "Errors encountered while parsing the level!");
          e.printStackTrace();
      }

    }
  catch (IOException e) {
      JOptionPane.showMessageDialog(null, "Errors encountered while parsing the level!");
    }
  catch (ParserConfigurationException e) {
      JOptionPane.showMessageDialog(null, "Errors encountered while parsing the level!");
    } catch (SAXException e) {
    	JOptionPane.showMessageDialog(null, "Errors encountered while parsing the level!");
}

      //sets the parsed instance as current instance in LevelController
      //the point of having those at the bottom is that none of the current
      //values are affected in case the file is damaged.
      levelModel.setLevel(parsedLevel);
      levelModel.setName(levelName);
      levelModel.setBackground(background);
      levelModel.setEvents(parsedEvents);
    }
 }

